package com.cs.deviceManager.mqtt.ota;

import cn.hutool.core.util.HexUtil;
import com.cs.deviceManager.mqtt.MqttUtil;
import com.cs.deviceManager.mqtt.ota.manager.OtaActiveManager;
import jakarta.annotation.Resource;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.*;
import java.util.zip.CRC32;

/**
 * 固件升级工具类（修复版）
 */
@Slf4j
@Component
@Scope("prototype")
public final class FirmwareUpdater implements AutoCloseable {

    // 协议常量
    private static final byte HEADER = (byte) 0xF1;
    private static final byte FUNCTION_REQUEST = 0x01;
    private static final byte FUNCTION_DATA = 0x02;
    private static final byte RES_REQUEST = (byte) 0x81;
    private static final byte RES_DATA = (byte) 0x82;
    private static final int PACKET_SIZE = 256;
    private static final int MAX_REQUEST_RETRIES = 50;

    // 设备ID
    public String deviceId;

    @Value("${mqtt.client-id:device-status-listener}")
    public String clientId;

    // 主题命名
    private String topicRequest;
    private String topicData;

    // 固件数据
    private byte[] firmwareData;
    private int totalPackets;
    private int currentPacketIndex = 0;

    // 状态控制
    private volatile boolean isDownloading = false;
    private volatile boolean hasStartedDataTransfer = false;
    private volatile int requestCount = 0;

    // 时间统计
    private long startTime;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, r -> {
        Thread t = new Thread(r);
        t.setName("FirmwareUpdater-" + t.getId());
        t.setDaemon(true);
        return t;
    });

    // 回调
    @Setter
    private UpdateCallback callback;

    // 粘包缓存
    private final ByteArrayOutputStream packetBuffer = new ByteArrayOutputStream(8192);

    // HTTP 客户端
    private final HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).followRedirects(HttpClient.Redirect.NORMAL).build();

    // ✅ 用于取消 request 重试任务
    private ScheduledFuture<?> requestFuture;

    // ✅ 注入管理器
    @Resource
    private OtaActiveManager otaActiveManager;

    // 构造函数
    public FirmwareUpdater() {
        System.out.println("🆕 FirmwareUpdater 构造: " + this.hashCode());
    }

    public void setDeviceId(String deviceId) {
        this.deviceId = deviceId;
        this.topicRequest = "device/%s/cmd".formatted(deviceId);
        this.topicData = "device/%s/cmd".formatted(deviceId);
        System.out.println("🔧 setDeviceId: " + deviceId);
    }

    /**
     * 开始固件更新
     */
    public void startUpdate(String firmwareSource) {
        if (isDownloading) {
            logMessage("固件更新正在进行中...");
            return;
        }
        // ✅ 注册自己为当前设备的 OTA 任务
        otaActiveManager.setActiveUpdater(deviceId, this);

        resetState();
        startTime = System.currentTimeMillis();
        logMessage("开始加载固件: %s".formatted(firmwareSource));

        try {
            byte[] firmware = downloadFirmware(firmwareSource);
            this.firmwareData = firmware;
            this.totalPackets = (int) Math.ceil((double) firmware.length / PACKET_SIZE);
            System.out.println("📊 固件大小: " + firmware.length + ", totalPackets: " + totalPackets + " | 实例: " + this.hashCode());
            logMessage("固件加载完成，大小: %d 字节，共 %d 包".formatted(firmware.length, totalPackets));
            sendFirmwareRequest();
        } catch (Exception e) {
            String errorMsg = "固件加载失败: " + e.getMessage();
            handleError(errorMsg);
        }
    }

    private byte[] downloadFirmware(String source) {
        try {
            if (source.startsWith("http://") || source.startsWith("https://")) {
                var request = HttpRequest.newBuilder().uri(URI.create(source)).timeout(Duration.ofSeconds(30)).GET().build();

                var response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
                if (response.statusCode() != 200) {
                    throw new IOException("HTTP " + response.statusCode());
                }
                return response.body();
            } else {
                return Files.readAllBytes(Path.of(source));
            }
        } catch (Exception e) {
            throw new RuntimeException("下载失败: " + e.getMessage(), e);
        }
    }

    /**
     * 发送固件更新请求（带重试，收到响应后取消）
     */
    private void sendFirmwareRequest() {
        if (scheduler.isShutdown() || scheduler.isTerminated()) {
            handleError("线程池已关闭，无法发送请求");
            return;
        }

        if (!isDownloading) {
            return;
        }

        if (requestCount >= MAX_REQUEST_RETRIES) {
            handleError("请求重试超限");
            return;
        }

        requestCount++;
        logMessage("发送固件请求 (#%d)".formatted(requestCount));

        byte[] requestPacket = createRequestPacket();
        System.out.println("发送数据 : " + HexUtil.encodeHexStr(requestPacket));
        publish(topicRequest, requestPacket);

        if (!scheduler.isShutdown()) {
            try {
                requestFuture = scheduler.schedule(this::sendFirmwareRequest, 1500, TimeUnit.MILLISECONDS);
            } catch (RejectedExecutionException e) {
                handleError("任务被拒绝: 线程池可能已关闭");
            }
        } else {
            handleError("无法调度重试任务：线程池已关闭");
        }
    }

    private byte[] createRequestPacket() {
        int len = firmwareData.length;
        int totalPackets = (int) Math.ceil((double) len / PACKET_SIZE);

        ByteBuffer buffer = ByteBuffer.allocate(12);
        buffer.put(HEADER).put(FUNCTION_REQUEST).putInt(len).putShort((short) totalPackets);

        byte[] header = new byte[8];
        buffer.position(0);
        buffer.get(header, 0, 8);

        long crc = calculateCRC32(header);
        buffer.putInt((int) crc);

        return buffer.array();
    }

    private void sendDataPacket(int index) {
        //System.out.println("📤 发送第 " + index + " 个数据包 | 实例: " + this.hashCode());
        byte[] packet = createDataPacket(index);
        publish(topicData, packet);
    }

    private byte[] createDataPacket(int index) {
        int start = index * PACKET_SIZE;
        int end = Math.min(start + PACKET_SIZE, firmwareData.length);
        byte[] data = Arrays.copyOfRange(firmwareData, start, end);

        byte[] header = ByteBuffer.allocate(4).put(HEADER).put(FUNCTION_DATA).putShort((short) index).array();

        byte[] source = ByteBuffer.allocate(header.length + data.length).put(header).put(data).array();

        long crc = calculateCRC32(source);
        return ByteBuffer.allocate(source.length + 4).put(source).putInt((int) crc).array();
    }

    private long calculateCRC32(byte[] data) {
        CRC32 crc = new CRC32();
        crc.update(data);
        return crc.getValue();
    }

    private void publish(String topic, byte[] payload) {
        if (scheduler.isShutdown() || scheduler.isTerminated()) {
            logMessage("线程池已关闭，无法发送消息");
            return;
        }

        try {
            boolean success = MqttUtil.publish(clientId, topic, payload);
            if (!success) {
                logMessage("MQTT 发布任务提交失败");
            }
        } catch (Exception e) {
            logMessage("MQTT 发送失败: " + e.getMessage());
        }
    }

    public void handleReceivedData(byte[] data) {
        try {
            packetBuffer.write(data);
        } catch (IOException e) {
            logMessage("缓冲写入失败");
            return;
        }
        parseBufferedData();
    }

    private void parseBufferedData() {
        byte[] buffer = packetBuffer.toByteArray();
        int index = 0;

        while (index < buffer.length) {
            if (buffer[index] == HEADER && index + 2 <= buffer.length) {
                byte func = buffer[index + 1];
                int packetLen = switch (func) {
                    case RES_REQUEST -> 12;
                    case RES_DATA -> 5;
                    default -> 5;
                };

                if (index + packetLen > buffer.length) break;

                byte[] packet = Arrays.copyOfRange(buffer, index, index + packetLen);
                index += packetLen;

                processPacket(packet);
            } else {
                index++;
            }
        }

        if (index > 0) {
            byte[] remaining = Arrays.copyOfRange(buffer, index, buffer.length);
            packetBuffer.reset();
            try (ByteArrayInputStream in = new ByteArrayInputStream(remaining)) {
                in.transferTo(packetBuffer);
            } catch (IOException ignored) {
            }
        }
    }

    private void processPacket(byte[] packet) {
        byte header = packet[0];
        byte func = packet[1];

        if (header != HEADER) return;

        if (func == RES_REQUEST && !hasStartedDataTransfer) {
            hasStartedDataTransfer = true;
            logMessage("设备已确认，开始发送固件数据");

            if (requestFuture != null) {
                requestFuture.cancel(false);
                requestFuture = null;
            }

            scheduler.execute(this::sendNextPacket);
            return;
        }

        if (func == RES_DATA) {
            int nextIndex = ++currentPacketIndex;
            int sentBytes = Math.min(nextIndex * PACKET_SIZE, firmwareData.length);
            int progress = Math.min(sentBytes * 100 / firmwareData.length, 100);
            int time = (int) ((System.currentTimeMillis() - startTime) / 1000);

            if (callback != null) {
                callback.onProgress(progress, sentBytes, firmwareData.length, time);
            }

            if (nextIndex >= totalPackets) {
                completeDownload();
            } else {
                sendNextPacket();
            }
        } else {
            String msg = switch (func) {
                case 0x02 -> "CRC校验错误";
                case 0x03 -> "长度不够";
                default -> "未知响应码: " + func;
            };
            logMessage(msg);
        }
    }

    private void sendNextPacket() {
        if (currentPacketIndex >= totalPackets) {
            completeDownload();
            return;
        }
        sendDataPacket(currentPacketIndex);
    }

    private void resetState() {
        isDownloading = true;
        currentPacketIndex = 0;
        requestCount = 0;
        hasStartedDataTransfer = false;
        packetBuffer.reset();
        requestFuture = null;
        if (callback != null) callback.onStart();
    }

    private void completeDownload() {
        isDownloading = false;
        int time = (int) ((System.currentTimeMillis() - startTime) / 1000);
        logMessage("固件更新完成，耗时 %d 秒".formatted(time));

        if (requestFuture != null) {
            requestFuture.cancel(false);
            requestFuture = null;
        }

        if (callback != null) callback.onComplete(time);

        // ✅ 注销
        otaActiveManager.removeActiveUpdater(deviceId);
        close();
    }

    public void handleError(String error) {
        isDownloading = false;
        logMessage("错误: %s".formatted(error));

        if (requestFuture != null) {
            requestFuture.cancel(false);
            requestFuture = null;
        }

        if (callback != null) callback.onError(error);

        // ✅ 注销
        otaActiveManager.removeActiveUpdater(deviceId);
        close();
    }

    @Override
    public void close() {
        if (requestFuture != null) {
            requestFuture.cancel(false);
            requestFuture = null;
        }
        // 不关闭 scheduler，由 JVM 回收 daemon 线程
    }

    // ======== 回调接口 ========
    public interface UpdateCallback {
        void onStart();

        default void onProgress(int progress, int sentBytes, int totalBytes, int elapsedTime) {
        }

        default void onComplete(int totalTime) {
        }

        default void onError(String error) {
        }

        default void onLog(String message) {
        }
    }

    private void logMessage(String message) {
        if (callback != null) {
            callback.onLog("[FOTA][%s] %s".formatted(deviceId, message));
        }
    }

}